package com.example.orchid.paper.http;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.text.TextUtils;

import com.example.orchid.paper.exception.PaperIOException;
import com.example.orchid.paper.exception.PaperOtherException;
import com.example.orchid.paper.util.Logger;
import com.example.orchid.paper.util.UrlUtils;

import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.NameValuePair;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.client.params.CookiePolicy;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.conn.params.ConnPerRouteBean;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.KeyStore;
import java.util.ArrayList;

import javax.net.ssl.SSLHandshakeException;

/**
 * HttpClient工具类
 * Created by Orchid on 2015-10-13.
 */
public class HttpClientUtils {
    private static final String CHARSET = HTTP.UTF_8;
    /**
     * 最大连接数
     */
    public final static int MAX_TOTAL_CONNECTIONS = 200;

    /**
     * 每个路由最大连接数
     */
    public final static int MAX_ROUTE_CONNECTIONS = 200;
    /**
     * 获取连接的最大等待时间
     */
    public final static int RETRIVE_CONNECT_TIMEOUT = 6 * 1000;
    private static final int RETRIED_TIME = 3;

    private static final int COMMON_TIMEOUT = 6 * 1000;

    private static Logger mLogger = Logger.getLogger();

    private static volatile DefaultHttpClient customerHttpClient = null;

    private HttpClientUtils() {
    }

    public static HttpClient getInstance(Context context) {
        if (customerHttpClient == null) {
            synchronized (HttpClientUtils.class) {
                if (customerHttpClient == null) {
                    HttpParams params = new BasicHttpParams();

                    // 设置一些基本参数
                    HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
                    HttpProtocolParams.setContentCharset(params, CHARSET);
                    HttpProtocolParams.setUseExpectContinue(params, true);
                    HttpProtocolParams.setUserAgent(params, System
                            .getProperties().getProperty("http.agent")
                            + " Mozilla/5.0 Firefox/26.0");

					/* 连接超时 */
                    HttpConnectionParams.setConnectionTimeout(params, COMMON_TIMEOUT);

					/* 请求超时 */
                    HttpConnectionParams.setSoTimeout(params, COMMON_TIMEOUT);
                    // 支持http和https两种模式
                    SchemeRegistry schReg = new SchemeRegistry();
                    schReg.register(new Scheme("http", PlainSocketFactory
                            .getSocketFactory(), 80));
                    schReg.register(new Scheme("https", getSSLSocketFactory(),
                            443));

                    // 使用线程安全的连接管理来创建HttpClient
                    ClientConnectionManager conMgr = new ThreadSafeClientConnManager(
                            params, schReg);

                    customerHttpClient = new DefaultHttpClient(conMgr, params);
                    customerHttpClient.setHttpRequestRetryHandler(requestRetryHandler);

                    ConnManagerParams.setMaxTotalConnections(params, MAX_TOTAL_CONNECTIONS);


                    // 设置每个路由最大连接数
                    ConnPerRouteBean connPerRoute = new ConnPerRouteBean(MAX_ROUTE_CONNECTIONS);
                    ConnManagerParams.setMaxConnectionsPerRoute(params, connPerRoute);

                    // 设置获取连接的最大等待时间
                    ConnManagerParams.setTimeout(params, RETRIVE_CONNECT_TIMEOUT);

                    ConnectivityManager manager = (ConnectivityManager) context
                            .getSystemService(Context.CONNECTIVITY_SERVICE);

                    NetworkInfo networkinfo = manager.getActiveNetworkInfo();
                    String net = networkinfo != null ? networkinfo
                            .getExtraInfo() : null;

                    // wifi的值为空
                    if (!TextUtils.isEmpty(net)) {
                        String proxyHost = getDefaultHost();

                        if (!TextUtils.isEmpty(proxyHost)) {
                            HttpHost proxy = new HttpHost(proxyHost, getDefaultPort(), "http");

                            customerHttpClient.getParams().setParameter(
                                    ConnRoutePNames.DEFAULT_PROXY, proxy);
                        }
                    }
                }
            }
        }
        return customerHttpClient;
    }

    private static SSLSocketFactory getSSLSocketFactory() {
        try {
            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            trustStore.load(null, null);

            SSLSocketFactory sslSocketFactory = new TrustAllSSLSocketFactory(trustStore);
            sslSocketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
            return sslSocketFactory;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 异常自动恢复处理, 使用HttpRequestRetryHandler接口实现请求的异常恢复
     */
    private static HttpRequestRetryHandler requestRetryHandler = new HttpRequestRetryHandler() {
        @Override
        public boolean retryRequest(IOException e, int i, HttpContext httpContext) {
            // 设置恢复策略，在发生异常时候将自动重试N次
            if (i >= RETRIED_TIME) {
                // Do not retry if over max retry count
                Logger.getLogger().e(
                        "Do not retry if over max retry count:"
                                + i);
                return false;
            }
            if (e instanceof NoHttpResponseException) {
                // Retry if the server dropped connection on us
                Logger.getLogger()
                        .i("Retry if the server dropped connection on us:exception instanceof NoHttpResponseException");
                return true;
            }
            if (e instanceof SSLHandshakeException) {
                // Do not retry on SSL handshake exception
                Logger.getLogger().e(
                        "Do not retry on SSL handshake SSLHandshakeException ");
                return false;
            }

            HttpRequest request = (HttpRequest) httpContext.getAttribute(ExecutionContext.HTTP_REQUEST);
            boolean idempotent = (request instanceof HttpEntityEnclosingRequest);
            if (!idempotent) {
                // Retry if the request is considered idempotent
                Logger.getLogger().i(
                        "Retry if the request is considered idempotent");
                return true;
            }
            return false;
        }
    };

    /**
     * get请求
     *
     * @param context
     * @param url
     * @param params
     * @param responseListener
     */
    public static void get(final Context context, String url, Bundle params,
                           ResponseListener responseListener) {
        if (params != null) {
            if (url.contains("?")) {
                url = url + "&" + UrlUtils.encodeUrl(params);
            } else {
                url = url + "?" + UrlUtils.encodeUrl(params);
            }
        }

        Logger.getLogger().d("GET:" + url);

        HttpGet get = new HttpGet(url);
        HttpClient client = getInstance(context);

        // 解决：HttpClient WARNING: Cookie rejected: Illegal domain attribute
        client.getParams().setParameter(ClientPNames.COOKIE_POLICY,
                CookiePolicy.BROWSER_COMPATIBILITY);

        String response = "";

        try {
            response = client.execute(get, new BasicResponseHandler());

            if (!TextUtils.isEmpty(response)) {
                responseListener.onComplete(response);
            } else {
                responseListener.onComplete("");
            }
        } catch (IOException e) {
            e.printStackTrace();

            if (e instanceof ConnectTimeoutException) {
                mLogger.e("error ConnectTimeoutException.");
            }
            mLogger.e("error begin.");
            mLogger.e("error params:");

            mLogger.e("error.url(GET):" + url);

            mLogger.e("error.getMessage:" + e.getMessage());

            mLogger.e("error end.");

            responseListener.onFail(new PaperIOException(
                    "request url IOException", e));
        } catch (Exception e) {
            e.printStackTrace();
            mLogger.e("error:" + e.getMessage());

            responseListener.onFail(new PaperOtherException(
                    "request url Exception", e));
        }
    }

    /**
     * get请求，返回字符串
     *
     * @param context
     * @param url
     * @param params
     * @return
     * @throws IOException
     * @throws Exception
     */
    public static String get(Context context, String url, Bundle params) throws IOException, Exception {
        if (params != null) {
            if (url.contains("?")) {
                url = url + "&" + UrlUtils.encodeUrl(params);
            } else {
                url = url + "?" + UrlUtils.encodeUrl(params);
            }
        }

        Logger.getLogger().d("GET:" + url);
        HttpGet get = new HttpGet(url);
        HttpClient client = getInstance(context);

        // 解决：HttpClient WARNING: Cookie rejected: Illegal domain attribute
        client.getParams().setParameter(ClientPNames.COOKIE_POLICY,
                CookiePolicy.BROWSER_COMPATIBILITY);

        return client.execute(get, new BasicResponseHandler());
    }

    /**
     * get a stream from web
     *
     * @param context
     * @param url
     * @param params
     * @return
     * @throws IOException
     * @throws Exception
     */
    public static InputStream getStream(Context context, String url, Bundle params)
            throws IOException, Exception {
        if (params != null) {
            if (url.contains("?")) {
                url = url + "&" + UrlUtils.encodeUrl(params);
            } else {
                url = url + "?" + UrlUtils.encodeUrl(params);
            }
        }
        Logger.getLogger().d("GET:" + url);
        HttpGet get = new HttpGet(url);
        HttpClient client = getInstance(context);

        // 解决：HttpClient WARNING: Cookie rejected: Illegal domain attribute
        client.getParams().setParameter(ClientPNames.COOKIE_POLICY,
                CookiePolicy.BROWSER_COMPATIBILITY);
        HttpResponse response = client.execute(get);
        return response.getEntity().getContent();
    }

    /**
     * post请求
     *
     * @param context
     * @param url
     * @param params
     * @param responseListener
     */
    public static void post(Context context, String url, ArrayList<NameValuePair> params,
                            ResponseListener responseListener) {
        mLogger.d("POST:" + url);

        try {
            UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params, CHARSET);
            HttpPost post = new HttpPost(url);
            post.setEntity(entity);

            HttpClient client = getInstance(context);
            HttpResponse response = client.execute(post);
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                HttpEntity resEntity = response.getEntity();

                String ret = (resEntity == null) ? null : EntityUtils.toString(resEntity, CHARSET);
                responseListener.onComplete(ret);
            } else {
                responseListener.onComplete("");
            }
        } catch (IOException e) {
            e.printStackTrace();
            mLogger.e("error.url(POST):" + url);

            responseListener.onFail(new PaperIOException(
                    "request url IOException", e));
        } catch (Exception e) {

            e.printStackTrace();
            mLogger.e("error:" + e.getMessage());
            responseListener.onFail(new PaperOtherException(
                    "request url Exception", e));
        }
    }

    /**
     * post请求，获取一个stream
     *
     * @param context
     * @param url
     * @param params
     * @return
     */
    public static InputStream post(Context context, String url, ArrayList<NameValuePair> params) {
        InputStream in = null;

        try {
            UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params, CHARSET);

            HttpPost post = new HttpPost(url);
            post.setEntity(entity);

            HttpClient client = getInstance(context);
            HttpResponse response = client.execute(post);

            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                HttpEntity resEntity = response.getEntity();

                in = (resEntity == null) ? null : resEntity.getContent();
            }
        } catch (IOException e) {

            e.printStackTrace();
            mLogger.e("error:" + e.getMessage());
        } catch (Exception e) {

            e.printStackTrace();
            mLogger.e("error:" + e.getMessage());
        }
        return in;
    }

    /**
     * post json字符串到服务器
     *
     * @param context
     * @param url
     * @param jsonString
     * @param responseListener
     */
    public static void post(Context context, String url, String jsonString,
                            ResponseListener responseListener) {

        try {
            HttpPost post = new HttpPost(url);

            StringEntity entity = new StringEntity(jsonString);
            post.setEntity(entity);
            post.setHeader("Accept", "application/json");
            post.setHeader("Content-type", "application/json");

            mLogger.d("POST:" + url);
            mLogger.d("BODY:" + jsonString);

            HttpClient client = getInstance(context);
            HttpResponse response = client.execute(post);
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                HttpEntity resEntity = response.getEntity();
                String ret = (resEntity == null) ? null : EntityUtils.toString(
                        resEntity, CHARSET);
                responseListener.onComplete(ret);
            } else {
                responseListener.onComplete("");
            }
        } catch (IOException e) {
            e.printStackTrace();
            mLogger.e("error begin.");
            mLogger.e("error POST BODY:");

            mLogger.e("error.url(POST):" + url);

            mLogger.e("error.getMessage:" + e.getMessage());

            mLogger.e("error end.");

            responseListener.onFail(new PaperIOException(
                    "request url IOException", e));
        } catch (Exception e) {

            e.printStackTrace();
            mLogger.e("error:" + e.getMessage());
            responseListener.onFail(new PaperOtherException(
                    "request url Exception", e));
        }
    }

    public static void post(Context context, String url, byte[] bytes,
                            ResponseListener responseListener) {

        HttpPost post = new HttpPost(url);
        post.setEntity(new ByteArrayEntity(bytes));


        try {
            mLogger.d("POST:" + url);

            HttpClient client = getInstance(context);
            HttpResponse response = client.execute(post);

            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                HttpEntity resEntity = response.getEntity();
                String ret = (resEntity == null) ? null : EntityUtils.toString(
                        resEntity, CHARSET);
                responseListener.onComplete(ret);
            } else {
                responseListener.onComplete("");
            }
        } catch (IOException e) {
            e.printStackTrace();
            mLogger.e("error.url(POST):" + url);
            responseListener.onFail(new PaperIOException(
                    "request url IOException", e));
        } catch (Exception e) {

            e.printStackTrace();
            mLogger.e("error:" + e.getMessage());
            responseListener.onFail(new PaperOtherException(
                    "request url Exception", e));
        }
    }

    private static String getDefaultHost() {
        return android.net.Proxy.getDefaultHost();
    }

    private static int getDefaultPort() {
        return android.net.Proxy.getDefaultPort();
    }
}
